//
// Created by entaoyang on 2019-02-13.
// Copyright (c) 2019 yet.net. All rights reserved.
//

import Foundation

#if os(iOS) || os(tvOS)
import UIKit
#else
import AppKit
#endif


public extension XImageView {
    fileprivate var httpUrl: String? {
        get {
            getAttr("__httpurl__") as? String
        }
        set {
            setAttr("__httpurl__", newValue)
        }
    }

    func loadUrl(_ url: String) {
        loadUrl(url) { _ in
        }
    }

    func loadUrl(_ url: String, onConfig: (HttpImage) -> Void) {
        self.httpUrl = url
        let h = HttpImage(url)
        h.displayView(self)
        onConfig(h)
        h.request()
    }
}

public typealias HttpImageViewCallback = (XImageView) -> Void
public typealias HttpImageCallback = (XImage?) -> Void
public typealias HttpImageFinishCallback = (HttpImage) -> Void

public class HttpImage {
    public let url: String
    private weak var imageView: XImageView? = nil
    private var imageViewCallback: HttpImageViewCallback? = nil
    private var imageCallback: HttpImageCallback? = nil
    private var finishCallback: HttpImageFinishCallback? = nil

    public var forceDownload: Bool = false
    public var failedImage = ""
    public var loadingImage = ""

    public var scaledWidth: CGFloat = 0
    public var scaledHeight: CGFloat = 0
    public var scaleCrop: Bool = true
    public var cacheToMem: Bool = true

    public var imageSetCallback: ((XImageView) -> Void)? = nil

    public init(_ url: String) {
        self.url = url
    }

    fileprivate func setImage(_ image: XImage?) {
        if let iv = self.imageView {
            iv.image = image
            imageSetCallback?(iv)
        }
    }


    fileprivate var key: String {
        "\(url)_\(scaledWidth)_\(scaledHeight)"
    }
}

public extension HttpImage {

    func displayView(_ imageView: XImageView) {
        self.imageView = imageView
    }

    func onImage(_ block: @escaping HttpImageCallback) {
        self.imageCallback = block
    }

    func onImageView(_ block: @escaping HttpImageViewCallback) {
        self.imageViewCallback = block
    }

    func onFinish(_ block: @escaping HttpImageFinishCallback) {
        self.finishCallback = block
    }


    func request() {
        if let img = MemCache.inst.get(self.key) as? XImage {
            requestResult2(img, toCache: false)
            return
        }
        if forceDownload {
            if let _ = self.imageView, loadingImage.notEmpty {
                setImage(XImage(named: loadingImage))
            }
            //use strong reference
            FileDownloader.inst.download(url) { file in
                if let d = file?.readData() {
                    self.requestResult(XImage(data: d))
                } else {
                    self.requestResult(nil)
                }
            }
        } else {
            //use strong reference
            FileDownloader.inst.retrive(url) { file in
                if let d = file?.readData() {
                    self.requestResult(XImage(data: d))
                } else {
                    self.requestResult(nil)
                }
            }
        }
    }

    private func requestResult(_ image: XImage?) {
//        logd("requestResult: ", image?.size)
        #if iOS
        if let img = image, (scaledWidth > 0 || scaledHeight > 0) {
            let a = img.imageScaleTo(width: scaledWidth, height: scaledHeight, crop: scaleCrop)
            requestResult2(a, toCache: cacheToMem)
        } else {
            requestResult2(image, toCache: cacheToMem)
        }
        #else
        requestResult2(image, toCache: cacheToMem)
        #endif
    }

    private func requestResult2(_ image: XImage?, toCache: Bool) {
        if let iv = self.imageView, iv.httpUrl == self.url {
            if let img = image {
                if toCache {
                    MemCache.inst.put(key: self.key, image: img)
                }
                setImage(img)
            } else if self.failedImage.notEmpty {
                setImage(XImage(named: failedImage))
            } else {
                setImage(nil)
            }
            if let c = self.imageViewCallback {
                c(iv)
            }
        }
        self.imageCallback?(image)
        self.finishCallback?(self)

        self.imageView = nil
        self.imageCallback = nil
        self.imageViewCallback = nil
        self.finishCallback = nil
    }


    static func batch(urls: [String], _ callback: (HttpImage) -> Void) {
        if urls.isEmpty {
            return
        }
        for url in urls {
            let h = HttpImage(url)
            callback(h)
            h.request()
        }
    }

}

#if iOS
fileprivate extension XImage {
    //width,height至少有一个不是0
    func imageScaleTo(width: CGFloat, height: CGFloat, crop: Bool) -> XImage {
        if width == 0 && height == 0 {
            fatalError("width and height both zero")
        }
        let oldW = self.size.width
        let oldH = self.size.height

        var newWidth: CGFloat = width
        var newHeight: CGFloat = height
        if width == 0 {
            newWidth = oldW / oldH * newHeight
        }
        if height == 0 {
            newHeight = oldH / oldW * newWidth
        }

        UIGraphicsBeginImageContext(Size(width: newWidth, height: newHeight))
        let _ = UIGraphicsGetCurrentContext()!


        var rect = Rect.zero
        var sf: Bool = oldW / oldH < newWidth / newHeight
        if crop {
            sf = oldW / oldH > newWidth / newHeight
        }
        if sf {
            let w = oldW / oldH * newHeight
            rect.size = Size(width: w, height: newHeight)
            rect.origin = CGPoint(x: -(w - newWidth) / 2, y: 0)
        } else {
            let h = oldH / oldW * newWidth
            rect.size = Size(width: newWidth, height: h)
            rect.origin = CGPoint(x: 0, y: -(h - newHeight) / 2)
        }
        logd(rect)
        self.draw(in: rect)
        let img = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return img
    }
}
#endif
